<?php

// ------------------------------------------------------------------------
// |@Author       : Jarmin <edshop@qq.com>
// |@----------------------------------------------------------------------
// |@Date         : 2025-04-25 10:54:44
// |@----------------------------------------------------------------------
// |@LastEditTime : 2025-04-26 23:32:14
// |@----------------------------------------------------------------------
// |@LastEditors  : Jarmin <jarmin@ladmin.cn>
// |@----------------------------------------------------------------------
// |@Description  : 插件助手函数
// |@----------------------------------------------------------------------
// |@FilePath     : Helper.php
// |@----------------------------------------------------------------------
// |@Copyright (c) 2025 http://www.ladmin.cn   All rights reserved.
// ------------------------------------------------------------------------

declare(strict_types=1);

use think\facade\Route;
use think\helper\{Str, Arr};
use think\App;

if (!function_exists('hook')) {
    /**
     * 处理插件钩子
     *
     * 用于触发插件事件并获取处理结果，支持多个监听器同时处理同一个事件。
     * 插件按类型（pre/normal/post）和优先级排序执行。
     *
     * @example
     * ```php
     * // 触发事件并获取所有监听器的处理结果
     * $result = hook('addon_init', $params);
     *
     * // 只获取第一个监听器的处理结果
     * $result = hook('addon_init', $params, true);
     * ```
     *
     * @param string $event 钩子名称，如：'addon_init', 'addon_install' 等
     * @param mixed $params 传入参数，可以是任意类型的数据
     * @param bool $once 是否只返回一个结果，默认为 false
     * @return array 所有监听器处理结果的数组
     */
    function hook(string $event, mixed $params = null, bool $once = false): array
    {
        $addons = get_addons_list();
        $result = [];

        // 按类型和优先级排序，保持键名
        uasort($addons, function ($a, $b) {
            $typeOrder = ['pre' => 1, 'normal' => 2, 'post' => 3];
            if ($a['type'] !== $b['type']) {
                return $typeOrder[$a['type']] - $typeOrder[$b['type']];
            }
            return $b['priority'] - $a['priority'];
        });

        foreach ($addons as $name => $addon) {
            if (!$addon['status'] || $addon['status'] === 0) {
                continue;
            }

            $class = get_addons_class($name);
            if (class_exists($class)) {
                $obj = new $class(app());
                if (method_exists($obj, $event)) {
                    $result[] = $obj->$event($params);
                    if ($once) {
                        break;
                    }
                }
            }
        }
        return $result;
    }
}

if (!function_exists('get_addons_list')) {
    /**
     * 获取插件列表
     *
     * 获取所有已安装的插件信息，包括插件名称、标题、描述、状态等。
     * 插件按目录扫描，自动加载插件信息。
     *
     * @example
     * ```php
     * // 获取所有插件列表
     * $addons = get_addons_list();
     * // 返回示例：
     * // [
     * //     'ipban' => [
     * //         'name' => 'ipban',
     * //         'title' => 'IP封禁插件',
     * //         'status' => 1,
     * //         'priority' => 10,
     * //         'type' => 'pre'
     * //     ],
     * //     'captcha' => [...]
     * // ]
     * ```
     *
     * @return array<string, array<string, mixed>> 插件列表数组，键名为插件名称
     */
    function get_addons_list(): array
    {
        $addons = [];
        $addonsPath = app()->getRootPath() . 'addons';

        if (!is_dir($addonsPath)) {
            return $addons;
        }

        $dirs = array_diff(scandir($addonsPath), ['.', '..']);
        foreach ($dirs as $dir) {
            if (!is_dir($addonsPath . DIRECTORY_SEPARATOR . $dir)) {
                continue;
            }

            $info = get_addons_info($dir);
            if (empty($info)) {
                continue;
            }

            $addons[$dir] = array_merge($info, [
                'priority' => $info['priority'] ?? 10,
                'type' => $info['type'] ?? 'normal',
                'status' => $info['status'] ?? 0
            ]);
        }

        return $addons;
    }
}

if (!function_exists('get_addons_info')) {
    /**
     * 读取插件的基础信息
     *
     * 获取插件的配置信息，包括插件名称、版本、作者等信息。
     * 信息从插件的 Plugin 类中获取。
     *
     * @example
     * ```php
     * // 获取插件信息
     * $info = get_addons_info('demo');
     * // 返回示例：
     * // [
     * //     'name' => 'demo',
     * //     'title' => '示例插件',
     * //     'version' => '1.0.0',
     * //     'author' => 'Jarmin'
     * // ]
     * ```
     *
     * @param string $name 插件名称，如：'demo'
     * @return array<string, mixed> 插件信息数组，如果插件不存在则返回空数组
     */
    function get_addons_info(string $name): array
    {
        $addon = get_addons_instance($name);
        return $addon ? $addon->getInfo() : [];
    }
}

if (!function_exists('get_addons_instance')) {
    /**
     * 获取插件的单例实例
     *
     * 获取插件的实例对象，如果实例已存在则直接返回，否则创建新实例。
     * 使用静态变量缓存实例，避免重复创建。
     *
     * @example
     * ```php
     * // 获取插件实例
     * $addon = get_addons_instance('demo');
     * // 调用插件方法
     * $addon->install();
     * ```
     *
     * @param string $name 插件名称，如：'demo'
     * @return \think\Addons|null 插件实例对象，如果插件不存在则返回 null
     * @throws \Exception 当插件实例创建失败时抛出异常
     */
    function get_addons_instance(string $name): ?\think\Addons
    {
        static $_addons = [];
        if (isset($_addons[$name])) {
            return $_addons[$name];
        }

        $class = get_addons_class($name);
        if (!$class || !class_exists($class)) {
            return null;
        }

        try {
            $_addons[$name] = new $class(app());
            return $_addons[$name];
        } catch (\Exception $e) {
            app()->log->error('Addon instance create failed: ' . $e->getMessage());
            return null;
        }
    }
}

if (!function_exists('get_addons_class')) {
    /**
     * 获取插件类的类名
     *
     * 根据插件名称和类型获取对应的类名，支持控制器和插件主类。
     * 自动处理命名空间和类名转换。
     *
     * @example
     * ```php
     * // 获取插件主类
     * $class = get_addons_class('demo');
     * // 返回：\addons\demo\Plugin
     *
     * // 获取插件控制器
     * $class = get_addons_class('demo', 'controller', 'index');
     * // 返回：\addons\demo\controller\Index
     * ```
     *
     * @param string $name 插件名称，如：'demo'
     * @param string $type 返回命名空间类型，可选值：'hook'|'controller'
     * @param string|null $class 当前类名，如：'index'，默认为插件名称
     * @return string 完整的类名，如果类不存在则返回空字符串
     */
    function get_addons_class(string $name, string $type = 'hook', ?string $class = null): string
    {
        $name = trim($name);
        $class = $class ?: $name;

        // 处理多级控制器情况
        if (strpos($class, '.')) {
            $class = explode('.', $class);
            $class[count($class) - 1] = Str::studly(end($class));
            $class = implode('\\', $class);
        } else {
            $class = Str::studly($class);
        }

        $namespace = match ($type) {
            'controller' => '\\addons\\' . $name . '\\controller\\' . $class,
            default => '\\addons\\' . $name . '\\Plugin'
        };

        // 匹配空控制器
        if ($type === 'controller' && !class_exists($namespace)) {
            $namespace = '\\addons\\' . $name . '\\Controller\\' . config('route.empty_controller');
        }

        return class_exists($namespace) ? $namespace : '';
    }
}

if (!function_exists('addons_url')) {
    /**
     * 生成访问插件的URL
     *
     * 根据插件名称、控制器和方法生成对应的URL地址。
     * 支持多种URL格式和参数配置。
     *
     * @example
     * ```php
     * // 生成当前插件的URL
     * $url = addons_url();
     *
     * // 生成指定插件的URL
     * $url = addons_url('demo/index/index');
     *
     * // 带参数的URL
     * $url = addons_url('demo/index/index', ['id' => 1]);
     *
     * // 使用scheme方式
     * $url = addons_url('demo://index/index');
     * ```
     *
     * @param string $url URL路径，支持多种格式：
     *                    - 空字符串：使用当前请求的插件、控制器和方法
     *                    - 'demo/index/index'：指定插件、控制器和方法
     *                    - 'demo://index/index'：使用scheme方式指定
     * @param array<string, mixed> $param URL参数，如：['id' => 1, 'name' => 'test']
     * @param bool|string $suffix 生成的URL后缀，true表示使用配置的后缀，false表示无后缀，字符串表示指定后缀
     * @param bool|string $domain 域名，true表示使用当前域名，false表示无域名，字符串表示指定域名
     * @return string 生成的URL地址
     */
    function addons_url(string $url = '', array $param = [], bool|string $suffix = true, bool|string $domain = false): string
    {
        $request = app('request');

        if (empty($url)) {
            // 生成 url 模板变量
            $addons = $request->addon;
            $controller = str_replace('/', '.', $request->controller());
            $action = $request->action();
        } else {
            $url = Str::studly($url);
            $url = parse_url($url);

            if (isset($url['scheme'])) {
                $addons = strtolower($url['scheme']);
                $controller = $url['host'];
                $action = trim($url['path'], '/');
            } else {
                $route = explode('/', $url['path']);
                $addons = $request->addon;
                $action = array_pop($route);
                $controller = array_pop($route) ?: $request->controller();
            }

            $controller = Str::snake((string)$controller);

            // 解析URL带的参数
            if (isset($url['query'])) {
                parse_str($url['query'], $query);
                $param = array_merge($query, $param);
            }
        }

        return app()->route->buildUrl("@addons/{$addons}/{$controller}/{$action}", $param)
            ->suffix($suffix)
            ->domain($domain);
    }
}
